/*******************************************************************************
* BSP
* ===
*
* The mainline of any test or demo needs to be devoid of hardware-specific
* functionality.  Instead general functions like `led_on()` or `start_adc()` or
* the like need to be used and the hardware implementation of these defined here
* in the BSP file.
*
* The absolute first thing that has to be provided is printf support so that
* program and demo outputs can be seen either on the console or on a serial port
* as appropriate.
*******************************************************************************/

#include <stdio.h>

#include "bsp.h"

#include "at32f435_437.h"

/***************************************
* Utility macros and constants.
***************************************/

#define STEP_DELAY_MS                    (uint32_t)(50)
#define TICK_COUNT_MAX                   (uint32_t)(0xFFFFFF)
#define TICK_COUNT_VALUE                 (SysTick->VAL)

/***************************************
* Static helper functions
***************************************/

static void bsp_nvic_config(void);
static void bsp_periph_clock_config(void);
static void bsp_system_clock_config(void);
static void bsp_timebase_init(void);
static void bsp_usart1_init(void);

/***************************************
* Global system-wide variables
***************************************/

volatile uint32_t systick_counter;

/***************************************
* Local static variables
***************************************/

/***************************************
* BSP functions for AT32F403AVG
***************************************/

void bsp_initialize()
{
    bsp_system_clock_config();
    bsp_periph_clock_config();
    bsp_nvic_config();
    bsp_timebase_init();
    bsp_usart1_init();
}

/***************************************
* Static function implementations
***************************************/

static void bsp_nvic_config(void)
{
    nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);

    NVIC_SetPriority(MemoryManagement_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_SetPriority(BusFault_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_SetPriority(UsageFault_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_SetPriority(SVCall_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_SetPriority(DebugMonitor_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_SetPriority(PendSV_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
}

static void bsp_periph_clock_config(void)
{
    crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_GPIOH_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);
}

static void bsp_system_clock_config(void)
{
    crm_reset();

    crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
    pwc_ldo_output_voltage_set(PWC_LDO_OUTPUT_1V3);
    flash_clock_divider_set(FLASH_CLOCK_DIV_3);

    pwc_battery_powered_domain_access(TRUE);

    if (crm_flag_get(CRM_LEXT_STABLE_FLAG) == RESET)
    {
        crm_clock_source_enable(CRM_CLOCK_SOURCE_LEXT, TRUE);

        while (crm_flag_get(CRM_LEXT_STABLE_FLAG) == RESET) { }
    }

    pwc_battery_powered_domain_access(FALSE);

    crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, FALSE);

    crm_clock_source_enable(CRM_CLOCK_SOURCE_LICK, TRUE);

    while (crm_flag_get(CRM_LICK_STABLE_FLAG) != SET) { }

    crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE);

    while (crm_hext_stable_wait() == ERROR) { }

    crm_clock_source_enable(CRM_CLOCK_SOURCE_HICK, TRUE);

    while (crm_flag_get(CRM_HICK_STABLE_FLAG) != SET) { }

    crm_pll_config(CRM_PLL_SOURCE_HEXT, 144, 1, CRM_PLL_FR_4);
    crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE);

    while (crm_flag_get(CRM_PLL_STABLE_FLAG) != SET) { }

    crm_ahb_div_set(CRM_AHB_DIV_1);
    crm_apb2_div_set(CRM_APB2_DIV_2);
    crm_apb1_div_set(CRM_APB1_DIV_2);

    crm_auto_step_mode_enable(TRUE);

    crm_sysclk_switch(CRM_SCLK_PLL);

    while (crm_sysclk_switch_status_get() != CRM_SCLK_PLL) { }

    crm_auto_step_mode_enable(FALSE);
    system_core_clock_update();
}

static void bsp_timebase_init(void)
{
    crm_clocks_freq_type crm_clocks;
    uint32_t frequency = 0, ticks = 0;

    crm_clocks_freq_get(&crm_clocks);
    frequency = crm_clocks.ahb_freq;

    systick_clock_source_config(SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV);

    ticks = frequency / 1000;

    SysTick->LOAD  = (uint32_t)(ticks - 1UL);
    SysTick->VAL   = 0UL;
    SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk |
                     SysTick_CTRL_ENABLE_Msk;
}

void bsp_usart1_init(void)
{
    gpio_init_type gpio_init_struct;
    gpio_default_para_init(&gpio_init_struct);

    gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE9, GPIO_MUX_7);
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
    gpio_init_struct.gpio_pins = GPIO_PINS_9;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init(GPIOA, &gpio_init_struct);

    gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE10, GPIO_MUX_7);
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
    gpio_init_struct.gpio_pins = GPIO_PINS_10;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init(GPIOA, &gpio_init_struct);

    usart_init(USART1, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);
    usart_transmitter_enable(USART1, TRUE);
    usart_receiver_enable(USART1, TRUE);
    usart_parity_selection_config(USART1, USART_PARITY_NONE);
    usart_hardware_flow_control_set(USART1, USART_HARDWARE_FLOW_NONE);

    usart_enable(USART1, TRUE);
}

/***************************************
* printf support
***************************************/

#if defined (__GNUC__) && !defined (__clang__)
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

/**
  * @brief  retargets the c library printf function to the usart.
  * @param  none
  * @retval none
  */
PUTCHAR_PROTOTYPE
{
#if !defined (__GNUC__) || defined (__clang__)
    UNUSED(f);
#endif

    while (usart_flag_get(USART1, USART_TDBE_FLAG) == RESET);

    usart_data_transmit(USART1, (uint16_t)ch);

    while (usart_flag_get(USART1, USART_TDC_FLAG) == RESET);

    return ch;
}

#if (defined (__GNUC__) && !defined (__clang__)) || (defined (__ICCARM__))
#if defined (__GNUC__) && !defined (__clang__)
int _write(int fd, char *pbuffer, int size)
#elif defined ( __ICCARM__ )
#pragma module_name = "?__write"
int __write(int fd, char *pbuffer, int size)
#endif
{
    UNUSED(fd);

    for (int i = 0; i < size; i ++)
    {
        while (usart_flag_get(USART1, USART_TDBE_FLAG) == RESET);

        usart_data_transmit(USART1, (uint16_t)(*pbuffer++));

        while (usart_flag_get(USART1, USART_TDC_FLAG) == RESET);
    }

    return size;
}
#endif

/***************************************
* ISRs
***************************************/

void NMI_Handler(void)
{
}

void HardFault_Handler(void)
{
    while (1) { }
}

void MemManage_Handler(void)
{
    while (1) { }
}

void BusFault_Handler(void)
{
    while (1) { }
}

void UsageFault_Handler(void)
{
    while (1) { }
}

void SVC_Handler(void)
{
}

void DebugMon_Handler(void)
{
}

void PendSV_Handler(void)
{
}

void SysTick_Handler(void)
{
    systick_counter++;
}
